package com.huangfu.learning.javabase.java.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created with IntelliJ IDEA.
 * Author: huangfu.daohui
 * Date: 2018/8/18
 * Time: 10:23
 * Version: 1.0
 * Description: 正则表达式：
 * 参考：https://blog.csdn.net/lyl0724/article/details/75574677
 * https://www.cnblogs.com/xyou/p/7427779.html
 * https://www.cnblogs.com/fozero/p/7868687.html
 * <p>
 * 主要包含三个类：
 * Pattern类：对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象，你必须首先调用其公共静态编译方法，它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
 * Matcher类：对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样，Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
 * PatternSyntaxException：是一个非强制异常类，它表示一个正则表达式模式中的语法错误。
 * <p>
 * 在 Java 的正则表达式中，两个 \\ 代表其他语言中的一个 \，这也就是为什么表示一位数字的正则表达式是 \\d，而表示一个普通的反斜杠是 \\\\。
 * 常用符号：
 * ^:匹配输入字符串开始的位置
 * $:匹配输入字符串结尾的位置
 * *:零次或多次匹配前面的字符或子表达式。例如，zo* 匹配"z"和"zoo"。* 等效于 {0,}。
 * +:一次或多次匹配前面的字符或子表达式。例如，"zo+"与"zo"和"zoo"匹配，但与"z"不匹配。+ 等效于 {1,}。
 * ?:零次或一次匹配前面的字符或子表达式。例如，"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1}。
 * {n}: n 是非负整数。正好匹配 n 次。例如，"o{2}"与"Bob"中的"o"不匹配，但与"food"中的两个"o"匹配。
 * {n,}:n 是非负整数。至少匹配 n 次。例如，"o{2,}"不匹配"Bob"中的"o"，而匹配"foooood"中的所有 o。"o{1,}"等效于"o+"。"o{0,}"等效于"o*"。
 * {n,m}:m 和 n 是非负整数，其中 n <= m。匹配至少 n 次，至多 m 次。例如，"o{1,3}"匹配"fooooood"中的头三个 o。'o{0,1}' 等效于 'o?'。注意：您不能将空格插入逗号和数字之间。
 * ?:当此字符紧随任何其他限定符（*、+、?、{n}、{n,}、{n,m}）之后时，匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串，而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。例如，在字符串"oooo"中，"o+?"只匹配单个"o"，而"o+"匹配所有"o"。
 * x|y:匹配 x 或 y。例如，'z|food' 匹配"z"或"food"。'(z|f)ood' 匹配"zood"或"food"。
 * [xyz]:字符集。匹配包含的任一字符。例如，"[abc]"匹配"plain"中的"a"。
 * [^xyz]:反向字符集。匹配未包含的任何字符。例如，"[^abc]"匹配"plain"中"p"，"l"，"i"，"n"。
 * [a-z]:字符范围。匹配指定范围内的任何字符。例如，"[a-z]"匹配"a"到"z"范围内的任何小写字母。
 * [^a-z]:反向范围字符。匹配不在指定的范围内的任何字符。例如，"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符。
 * \d:数字字符匹配。等效于 [0-9]。
 * \D:非数字字符匹配。等效于 [^0-9]。
 * \s:空白字符，[ \t\n\x0B\f\r]
 * \S:非空白符
 * \w:大小写字母和下斜杠，同[a-zA-Z_0-9]
 * \W:[^\w]
 * (pattern): 匹配 pattern 并捕获该匹配的子表达式。可以使用 $0…$9 属性从结果"匹配"集合中检索捕获的匹配。若要匹配括号字符 ( )，请使用"\("或者"\)"。
 **/
public class RegexPractice {

    public static void main(String[] args) {
        System.out.println("---------------匹配--------------------");
        matchesRegex(); // 匹配
        System.out.println("---------------分割--------------------");
        splitRegex(); // 分割
        System.out.println("---------------组分割--------------------");
        splitGroupRegex(); // 组分割
        System.out.println("---------------替换--------------------");
        replaceAllRegex();// 替换
        System.out.println("----------------组替换-------------------");
        replaceAllGroupRegex();//组替换
        System.out.println("----------------获取-------------------");
        getRegex(); //获取
    }

    /**
     * 匹配：matches
     */
    public static void matchesRegex() {

        String qq = "2005067088"; // qq号
        String regex = "[1-3]\\d{4,9}"; // 第一位是1-3，加上4-9位数字

        boolean flag = qq.matches(regex);
        if (flag) {
            System.out.println(qq + "...is ok");
        } else {
            System.out.println(qq + "... 不合法");
        }

        // 手机号匹配 1开头，第二位是3、5、8，然后在匹配9位数字
        String tel = "13900001111";
        String telReg = "1[358]\\d{9}";
        System.out.println(tel.matches(telReg));
    }

    /**
     * 分割
     */
    public static void splitRegex() {
        String str = "avg   bb   geig   glsd   abc h";
        String reg = " +";//按照多个空格来进行切割
        String[] arr = str.split(reg);
        for (String s : arr) {
            System.out.println(s);
        }
    }

    /**
     * 根据组分割，即连续相同的字符为分隔符
     */
    public static void splitGroupRegex() {
        String str = "erkktyqqquizzzzzo";
        String reg = "(.)\\1+";//按照叠词来进行切割
        //可以将规则封装成一个组。用()完成。组的出现都有编号。
        //从1开始。 想要使用已有的组可以通过  \n(n就是组的编号)的形式来获取。
        String[] arr = str.split(reg);
        for (String s : arr) {
            System.out.println(s);
        }
    }

    /**
     * 替换replaceAll(regex,str)
     * 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
     * ps：如果regex中有定义组，可以在第二参数中通过$符号获取正则表达式中的已有的组。
     */
    public static void replaceAllRegex() {
        String str = "wer1389980000ty1234564uiod234345675f";//将字符串中的数字替换成#。

        str = str.replaceAll("\\d{5,}", "#");

        System.out.println(str);
    }

    /**
     * 组group
     */
    public static void replaceAllGroupRegex() {

        String str = "erkktyqqquizzzzzo";//将叠词替换成$.  //将重叠的字符替换成单个字母。zzzz->z
        str = str.replaceAll("(.)\\1+", "$1");
        System.out.println(str);
    }

    /**
     * 获取
     * ----将字符串中的符合规则的子串取出。
     * 操作步骤：
     * 1，将正则表达式封装成对象。
     * 2，让正则对象和要操作的字符串相关联。
     * 3，关联后，获取正则匹配引擎。
     * 4，通过引擎对符合规则的子串进行操作，比如取出。
     */
    public static void getRegex() {
        String str = "yin yu shi wo zui cai de yu yan";
        System.out.println(str);
        String reg = "\\b[a-z]{3}\\b";//匹配只有三个字母的单词

        //将规则封装成对象。
        Pattern p = Pattern.compile(reg);

        //让正则对象和要作用的字符串相关联。获取匹配器对象。
        Matcher m = p.matcher(str);

        System.out.println(m.matches());//其实String类中的matches方法。用的就是Pattern和Matcher对象来完成的。
        //只不过被String的方法封装后，用起来较为简单。但是功能却单一。

         boolean b = m.find();//将规则作用到字符串上，并进行符合规则的子串查找。
         System.out.println(b);
         System.out.println(m.group());//用于获取匹配后结果。


        while (m.find()) {
            System.out.println(m.group());
            System.out.println(m.start() + "...." + m.end());
            // start()  字符的开始下标（包含）
            //end()  字符的结束下标（不包含）
        }
    }
}
